package com.gitee.wsl.eventbus.event4k

import com.gitee.wsl.struct.register.RegisterHook
import kotlinx.coroutines.sync.Mutex

class Event4kConcurrentBagImp: Event4kConcurrentBag {
    private val multiPlatformEvent4kConcurrentBag = MultiPlatformEvent4kMutexLockingConcurrentBag()

     override suspend fun <T> computeFromKeyIfExist(key: String, computer: (BagValue) -> T): T? =
        multiPlatformEvent4kConcurrentBag.computeFromKeyIfExist(key, computer)

     override suspend fun <T> update(key: String, computer: (BagValue) -> Pair<BagValue, T>): T =
        multiPlatformEvent4kConcurrentBag.updateAndGet(key, computer)

     override suspend fun removeRegisterHook(key: String, hook: RegisterHook<Any?>) =
        multiPlatformEvent4kConcurrentBag.removeRegisterHook(key, hook)

     override suspend fun removeAll() =
        multiPlatformEvent4kConcurrentBag.removeAll()

    override fun isExits(key: String): Boolean {
        return multiPlatformEvent4kConcurrentBag.isExits(key)
    }

}


internal class MultiPlatformEvent4kMutexLockingConcurrentBag{

    private val bag: MutableMap<String, BagValue> = mutableMapOf()
    private val bagMutex = Mutex()

    private suspend fun <T> acquireLockOfBagAndDo(f: () -> T): T {
        bagMutex.lock()
        val res = f()
        bagMutex.unlock()
        return res
    }
    suspend fun <T> computeFromKeyIfExist(key: String, computer: (BagValue) -> T): T?{
        return acquireLockOfBagAndDo {
            bag[key]?.let(computer)
        }
    }
    suspend fun <T> updateAndGet(key: String, computer: (BagValue) -> Pair<BagValue,T> ): T{
        return acquireLockOfBagAndDo {
            val res = computer(bag.getOrPut(key){ mutableMapOf()})
            bag[key] = res.first
            res.second
        }
    }

    suspend fun removeRegisterHook(key: String, hook: RegisterHook<Any?>){
        acquireLockOfBagAndDo {
            bag[key]?.let {
                it.remove(hook)
                if (it.isEmpty())
                    bag.remove(key)
            }
        }
    }

    suspend fun removeAll(){
        acquireLockOfBagAndDo {
            bag.clear()
        }
    }

    fun isExits(key: String): Boolean {
        return bag.contains(key)
    }
}